package com.phoenix.jsef.framework;

import com.phoenix.jsef.common.CUtils;
import com.phoenix.jsef.common.ConvertUtils;
import com.phoenix.jsef.common.ReflectionUtils;
import com.phoenix.jsef.common.XmlUtils;
import com.phoenix.jsef.framework.annotations.EntityCollectionAttribute;
import com.phoenix.jsef.framework.annotations.EntityFieldAttribute;
import com.phoenix.jsef.jdbcutils.DBTable;

import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.*;


/**
 * 实体对象转换器
 * @author 李启联
 *
 */
public class EntityConvertor {
	
	/**
	 * 把Map对象转换为对应实体类型对象，支持级联对象
	 * @param map map对象
	 * @param type 实体类型
	 * @return 实体对象
	 * @throws Exception
	 */
	public static Entity dataMapToEntity(Map<String, Object> map, Type type) throws Exception {
		
		if (null == map || map.isEmpty())
			return null;
		
		Entity entity = (Entity) Class.forName(type.getTypeName()).newInstance();
		String fieldName = "";
		Object obj =null;
		Class elementType = null;		
		Field[] fields = Class.forName(type.getTypeName()).getDeclaredFields();

		for (Field field : fields) { // 遍历所有属性
			fieldName = field.getName(); // 获取属性的名字
			field.setAccessible(true);
			
			if(ReflectionUtils.isStaticFinalField(field))//如果该字段使用了static final 修饰 【通常这么修饰的字段是用来定义常量的】
				continue;//	遍历下一个
			
			if(field.isAnnotationPresent(EntityFieldAttribute.class)) {
				EntityFieldAttribute fieldAttr = field.getAnnotation(EntityFieldAttribute.class);
				if(fieldAttr.aliasName().length() > 0)//如果属性标注了别名
					fieldName = fieldAttr.aliasName();
			}
					
			obj = CUtils.getValueFromMapByLikeKey(fieldName, map);//平行查找与属性名相同键的Map对应的值			
			if (field.getType() == EntityCollection.class) {// 如果属性是另外类型实体的集合		
				EntityCollectionAttribute attribute = (EntityCollectionAttribute) field
						.getAnnotation(EntityCollectionAttribute.class);
			    elementType = attribute.elementType();
			    if(null != obj && null != elementType ) {
			    	 if (obj instanceof List || List.class.isAssignableFrom(obj.getClass())){
			    		 EntityCollection collection = new EntityCollection();
						 for(Map<String, Object> subMap : (List<Map<String, Object>>)obj) {
							 collection.add(dataMapToEntity(subMap,elementType));
						 }
						 field.set(entity,collection);
			    	 }			    	
			    }				
			} else if (Collection.class.isAssignableFrom(field.getType())//field.getType().isAssignableFrom(List.class)
					&& Entity.class.isAssignableFrom(ReflectionUtils.getFieldClass(field))) { // 如果属性是另外类型实体的泛型集合
				// field.set(entity, fillListWithGenericParam(entity, field, map));
				if (null != obj) {
					 if (obj instanceof Collection || Collection.class.isAssignableFrom(obj.getClass())){
                         Collection collection ;

                         if(Set.class.isAssignableFrom(obj.getClass())){
					        collection = new HashSet();
                         }else {
                             collection = new ArrayList();
                         }
						 elementType = ReflectionUtils.getFieldClass(field);// 得到Collection泛型参数类型
						 for(Map<String, Object> subMap : (Collection<Map<String, Object>>)obj) {
							 collection.add(dataMapToEntity(subMap,elementType));
						 }
						 field.set(entity,collection);
					 }
				}
			} else if (field.getType().getSuperclass() == Entity.class) {// 如果属性是另外类型的单个实体
				if(null != obj) {
					if(obj.getClass().isAssignableFrom(Map.class))
						field.set(entity,dataMapToEntity((Map<String, Object>)obj,field.getType()));
				}
			} else {// 如果属性类型是 'string','Date','int', 'long','Double'......			    
				if (null != obj && obj.toString().length() > 0)
					// field.set(entity, obj);
					field.set(entity, ConvertUtils.convertObjectToTargetType(obj, field.getType()));
			}
		}
		return entity;
	}
	
	/**
	 * Map集合数组转换为对应实体集合
	 * @param mapList Map集合数组
	 * @param type 实体类型
	 * @return
	 * @throws Exception
	 */
	public static List<?> dataMapListToEntityList(List<Map<String, Object>> mapList, Type type) throws Exception {
		 List collection = new ArrayList();
		 for(Map<String, Object> map : mapList) {
			 collection.add(dataMapToEntity(map,type));
		 }
		return collection;
	}
	
	/**
	 * 把xml文档内容转化为对应实体类型的集合
	 * @param xmlStr xml文档内容
	 * @param xPathExpress xPath 表达式  节点查找表达式
	 * @param type 实体类型 
	 * @return
	 * @throws Exception
	 */
	public static List<?> xmlToEntityList(String xmlStr,String xPathExpress,Type type) throws Exception{
		List<Map<String, Object>> mapList = XmlUtils.convertXmlToMapList(xmlStr, xPathExpress);
		return dataMapListToEntityList(mapList,type);
	}
	
	/**
	 * 把xml文档内容转化为对应实体
	 * @param xmlStr xml文档内容
	 * @param xPathExpress xPath 表达式  节点查找表达式
	 * @param type 实体类型 
	 * @return
	 * @throws Exception
	 */
	public static Entity xmlToEntity(String xmlStr,String xPathExpress,Type type) throws Exception{		
		List list = xmlToEntityList(xmlStr,xPathExpress,type);
		if(!list.isEmpty())
			return (Entity)list.get(0);			
		return null;
	}


	/**
	 * 将数据库返回的数据表映射到对应的实体上
	 * @param dbTable 数据库返回的数据表对象
	 * @param type 实体类型
	 * @return 实体对象
	 * @throws Exception
	 */
	public static List<?> dbTableToEntityList(DBTable dbTable,Type type) throws Exception{

		if(null==dbTable)
			return null;
        List list = new ArrayList<>();
		List<Map<String, Object>> dataRows = dbTable.getData();
		for (Map<String, Object> row : dataRows) {
			Entity entity = dataRowToEntity(row,type);
			list.add(entity);
		}

		return list;
	}


	/**
	 * 将返回的数据[DataRow]填充到实体中
	 * @param map map对象通常为数据库中对应的DataRow
	 * @param type 实体类型
	 * @return 实体对象
	 * @throws Exception
	 */
	public static Entity dataRowToEntity(Map<String, Object> map, Type type) throws Exception{
		if (null == map || map.isEmpty())
			return null;

		Entity entity = (Entity) Class.forName(type.getTypeName()).newInstance();
		String fieldName = "",columnName = "";
		Object obj =null;
		Field[] fields = Class.forName(type.getTypeName()).getDeclaredFields();

		for (Field field : fields){
			if(ReflectionUtils.isStaticFinalField(field))//如果该字段使用了static final 修饰 【通常这么修饰的字段是用来定义常量的】
				continue;//	遍历下一个
			fieldName = field.getName(); // 获取属性的名字
			columnName = CUtils.camelToUnderline(fieldName);// 把属性名称的驼峰命名转换成数据库中下划线命名的列名

			EntityFieldAttribute filedAttribute = field.getAnnotation(EntityFieldAttribute.class);
			if (filedAttribute != null && filedAttribute.columnName() != null)// 如果在实体中标记了其在数据库中对应的列名称则优先用此标记的名称
				columnName = filedAttribute.columnName().toLowerCase();

			field.setAccessible(true);

			//忽略实体内部对应以下的复合对象，因为这是一个简单的单纯地将返回的数据[DataRow]填充到实体中
			if(field.getType() == EntityCollection.class || Entity.class.isAssignableFrom(field.getType()) || field.getType().isAssignableFrom(List.class))
				continue;

			if (field.getType() != EntityCollection.class && !Entity.class.isAssignableFrom(field.getType())
					&& !field.getType().isAssignableFrom(List.class)) { // 之所以加这层判断是为了防止此类型的属性命名时和数据库中表对象的字段有对应关系的冲突
				if ((map.containsKey(columnName) && map.get(columnName) == null) || !map.containsKey(columnName))
					continue;
			}

			if (map.containsKey(columnName) && map.get(columnName) != null) {
				field.set(entity, ConvertUtils.convertObjectToTargetType(map.get(columnName), field.getType()));
			}

		}

		return entity;
	}

}
